import { BaseLayer, IBaseLayerParams, RenderOptions } from './BaseLayer';
import {
  DataDrivenPropertyValueSpecification,
  FilterSpecification,
  LayerSpecification,
} from 'mapbox-gl';
import logger from 'loglevel';
import { MatchBuilder } from '..';

type TextStyle = {
  'font-scale'?: number;
  'text-color'?: string;
  'text-font'?: string[];
};

export class TextFragment {
  property?: string;
  style?: TextStyle = {};
  value?: string;
  private textBuilder?: TextBuilder;
  hide: [string, string | number] | undefined;
  constructor(
    params: { property?: string; style?: TextStyle; value?: string } = {},
  ) {
    const { property, style, value } = params;
    this.value = value;
    if (!value) {
      this.property = property;
    }
    this.style = style;
  }

  static fromProperty(property: string) {
    const fragment = new TextFragment();
    return fragment.useProperty(property);
  }

  static fromValue(value: string) {
    const fragment = new TextFragment();
    return fragment.useValue(value);
  }

  setTextBuilder(tb: TextBuilder) {
    this.textBuilder = tb;
    return this;
  }

  end() {
    return this.textBuilder;
  }

  useProperty(property: string) {
    this.property = property;
    return this;
  }

  useValue(value: string) {
    this.value = value;
    return this;
  }

  setStyle(style: TextStyle) {
    this.style = style;
    return this;
  }

  hideWhen(property: string, value: string | number) {
    this.hide = [property, value];
    return this;
  }
}

export class TextBuilder {
  private textFragments: TextFragment[] = [];
  constructor(text: TextFragment[] = []) {
    this.textFragments = text;
  }
  addText() {
    const fragment = new TextFragment();
    fragment.setTextBuilder(this);
    this.textFragments.push(fragment);
    return fragment;
  }
  format(): DataDrivenPropertyValueSpecification<string> {
    let temp: any[] = [];
    this.textFragments.forEach((textFragment) => {
      if (textFragment.value) {
        temp.push(textFragment.value);
      } else if (textFragment.hide) {
        const [property, value] = textFragment.hide;
        temp.push(
          MatchBuilder.match(property)
            .case(value, '')
            .default(['get', textFragment.property])
            .format(),
        );
      } else {
        temp.push(['get', textFragment.property]);
      }
      if (textFragment.style) {
        temp.push(textFragment.style);
      }
    });
    temp = temp.length > 0 ? temp : [''];
    const format = ['format', ...temp];
    logger.info(format, 'text format');
    return format as DataDrivenPropertyValueSpecification<string>;
  }
}

export class TextLayer extends BaseLayer {
  private _filter?: FilterSpecification;
  // private textBuilder: TextBuilder;
  private options: RenderOptions = {};
  constructor(cfg: IBaseLayerParams & { options?: RenderOptions }) {
    super(cfg);
    this.options = cfg.options || {};
    // if (filter) {
    //   this._filter = filter.format();
    // }
    // this.textBuilder = tb;
    // this._filter = ['in', ['get', 'type'], 'heatplant'];
  }

  filter(conditions: FilterSpecification) {
    return this.map.setFilter(this.layerId, conditions);
  }

  addToMap(): string {
    // const textField = this.textBuilder.format();
    const layout = (this.options.layout as LayerSpecification['layout']) || {};
    const paint = (this.options.paint as LayerSpecification['paint']) || {};
    const option = {
      id: this.layerId,
      type: 'symbol',
      source: this.sourceId,
      layout,
      paint,
    } as LayerSpecification;
    if (this._filter) {
      option.filter = this._filter;
    }
    this.map.addLayer(option);
    return this.layerId;
  }
}
